import aiohttp
import ssl
from datetime import datetime, timedelta
from typing import Optional, Dict
from loguru import logger
from core.platform_config import platform_config
from Crypto.Cipher import PKCS1_v1_5
from Crypto.PublicKey import RSA
import base64
from functools import wraps
import json
import os
from PyQt5.QtWidgets import QMessageBox, QWidget
from PyQt5.QtCore import QMetaObject, Qt, Q_ARG, QTimer
import requests
import asyncio

def log_exception(func):
    """异常日志装饰器"""
    @wraps(func)
    async def wrapper(*args, **kwargs):
        try:
            return await func(*args, **kwargs)
        except Exception as e:
            logger.exception(f"{func.__name__} 执行出错: {str(e)}")
            raise
    return wrapper

class PlatformAuth:
    def __init__(self):
        self.config = platform_config.dahua
        self.token = None
        self.token_expires_at = None
        self._session: Optional[aiohttp.ClientSession] = None
        self._initialized: bool = False
        # 添加消息队列
        self._pending_messages = []
        
    def show_message(self, title, text):
        """显示消息弹窗"""
        # 使用 QTimer 在主线程中显示消息
        QTimer.singleShot(0, lambda: self._show_message_impl(title, text))
    
    def _show_message_impl(self, title, text):
        """实际显示消息的实现"""
        msg_box = QMessageBox()
        msg_box.setWindowTitle(title)
        msg_box.setText(text)
        msg_box.setIcon(QMessageBox.Information)
        msg_box.show()  # 使用 show() 替代 exec_()

    async def initialize(self):
        """初始化认证服务"""
        if self._initialized:
            return
            
        try:
            logger.info("正在初始化大华平台认证服务...")
            # 获取session
            session = await self.get_session()
            
            # 初始获取token
            token = await self.get_dahua_token(force_refresh=True)
            if not token:
                raise Exception("初始化大华平台token失败")
                
            self._initialized = True
            logger.info("大华平台认证服务初始化完成")
            self.show_message("认证成功", "大华平台认证服务初始化完成")
            
        except Exception as e:
            logger.error(f"初始化大华平台认证服务失败: {str(e)}")
            self.show_message("认证失败", f"初始化大华平台认证服务失败: {str(e)}")
            # 确保关闭session
            await self.close()
            raise

    async def shutdown(self):
        """安全关闭所有连接"""
        logger.info("开始关闭认证服务...")
        if self._session and not self._session.closed:
            try:
                logger.info("正在关闭session...")
                # 先关闭连接器
                if not self._session._connector.closed:
                    await self._session._connector.close()
                
                # 等待一小段时间确保连接完全关闭
                await asyncio.sleep(0.1)
                
                # 关闭会话
                await self._session.close()
                self._session = None
                self._initialized = False
                logger.info("认证服务关闭完成")
            except Exception as e:
                logger.error(f"关闭session时发生错误: {e}")
                # 确保session被标记为None，即使发生错误
                self._session = None
            finally:
                # 确保标记为未初始化
                self._initialized = False

    async def get_session(self) -> aiohttp.ClientSession:
        """获取或创建 HTTP 会话"""
        if self._session is None or self._session.closed:
            # 配置 SSL 上下文
            ssl_context = ssl.create_default_context()
            if not self.config.verify_ssl:
                ssl_context.check_hostname = False
                ssl_context.verify_mode = ssl.CERT_NONE
            
            # 配置连接器
            connector = aiohttp.TCPConnector(
                ssl=ssl_context,
                force_close=True,  # 强制关闭连接
                enable_cleanup_closed=True,  # 启用清理已关闭的连接
                ttl_dns_cache=300  # DNS 缓存时间
            )
            
            timeout = aiohttp.ClientTimeout(total=self.config.ssl_timeout)
            self._session = aiohttp.ClientSession(
                connector=connector,
                timeout=timeout,
                trust_env=True
            )
        return self._session

    async def close(self):
        """关闭会话"""
        await self.shutdown()
    
    @log_exception
    async def get_dahua_token(self, force_refresh: bool = False) -> Optional[str]:
        """获取大华平台的访问令牌"""
        # 如果token未过期且不强制刷新，直接返回
        if not force_refresh and self.token and self.token_expires_at and self.token_expires_at > datetime.now():
            logger.debug(f"使用现有token, 过期时间: {self.token_expires_at}")
            return self.token

        try:
            session = await self.get_session()
            
            # 获取 public_key
            public_key_url = f"{self.config.api_host}{self.config.public_key_path}"
            logger.info(f"[认证] 开始获取公钥，URL: {public_key_url}")
            logger.info(f"[认证] 配置信息: host={self.config.api_host}, username={self.config.username}")
            
            async with session.get(public_key_url, ssl=False, timeout=self.config.ssl_timeout) as response:
                logger.info(f"[认证] 公钥请求状态码: {response.status}")
                response_text = await response.text()
                logger.info(f"[认证] 公钥响应内容: {response_text}")
                
                if response.status != 200:
                    logger.error(f"[认证] 获取public_key失败，状态码: {response.status}，响应: {response_text}")
                    return None
                    
                result = await response.json()
                if not result.get("success"):
                    logger.error(f"[认证] 获取public_key失败，返回结果: {result}")
                    return None

                pubkey = result.get("data", {}).get("publicKey", "")
                logger.info(f"[认证] 成功获取公钥: {pubkey[:30]}...")
                
            # 加密密码
            pub_key = "-----BEGIN PUBLIC KEY-----\n" + pubkey.strip('"') + "\n-----END PUBLIC KEY-----"
            logger.debug(f"[认证] 开始加密密码...")
            key_obj = RSA.importKey(pub_key)
            cipher = PKCS1_v1_5.new(key_obj)
            encrypted_pwd = base64.b64encode(cipher.encrypt(self.config.password.encode())).decode()
            logger.info("[认证] 密码加密完成")
            
            # 构建认证请求
            auth_data = {
                "grant_type": "password",
                "username": self.config.username,
                "password": encrypted_pwd,
                "client_id": self.config.client_id,
                "client_secret": self.config.client_secret,
                "public_key": pubkey
            }
            logger.info(f"[认证] 准备发送认证请求: {self.config.api_host}{self.config.auth_path}")
            
            # 请求令牌
            async with session.post(
                f"{self.config.api_host}{self.config.auth_path}", 
                json=auth_data,
                ssl=False
            ) as response:
                logger.info(f"[认证] 认证请求状态码: {response.status}")
                response_text = await response.text()
                logger.info(f"[认证] 认证响应内容: {response_text}")
                
                if response.status != 200:
                    logger.error(f"[认证] 获取token失败，状态码: {response.status}，响应: {response_text}")
                    return None
                    
                result = await response.json()
                if not result.get("success"):
                    logger.error(f"[认证] 获取token失败，返回结果: {result}")
                    return None
                    
                token_data = result["data"]
                self.token = token_data["access_token"]
                expires_in = int(token_data.get("expires_in", 7200))
                self.token_expires_at = datetime.now() + timedelta(seconds=expires_in)
                
                logger.info(f"[认证] 成功获取新token，过期时间: {self.token_expires_at}")
                self.show_message("认证成功", "成功获取访问令牌")
                return self.token
                
        except Exception as e:
            logger.exception(f"[认证] 获取token过程发生异常: {str(e)}")
            self.show_message("认证失败", f"获取token失败: {str(e)}")
            return None
            
    @log_exception
    async def get_dahua_headers(self) -> Dict[str, str]:
        """获取大华平台请求头"""
        token = await self.get_dahua_token()
        if not token:
            raise Exception("无法获取访问令牌")
            
        return {
            "Authorization": f"bearer {token}",
            "Content-Type": "application/json;charset=UTF-8",
            "Accept": "application/json, text/plain, */*",
            "Accept-Language": "zh-CN",
            "User-Client": "1"
        }

# 创建全局认证服务实例
platform_auth = PlatformAuth() 

class DahuaAuth:
    # ... 现有代码 ...
    
    def get_token(self):
        # 现有获取 token 的代码
        pass

# 添加便捷函数
def get_dahua_token():
    auth = DahuaAuth()
    return auth.get_token() 

def get_dahua_headers():
    response = requests.get(
        url=f"{platform_config.dahua.api_host}{platform_config.dahua.auth_path}",
        verify=platform_config.dahua.verify_ssl
    )
    # ... 其他代码 ... 